/*-------------------------------------------------------------------------
 *
 * tuptable.h
 *	  元组表支持
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/executor/tuptable.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef TUPTABLE_H
#define TUPTABLE_H

#include "access/htup.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/tupdesc.h"
#include "storage/buf.h"

/*----------
 * 执行器将元组存储在一个“元组表”中，该表是独立TupleTableSlots的列表。
 *
 * 元组表槽有各种不同类型，每种类型能够存储不同类型的元组。可以在不修改核心代码的情况下添加额外类型的槽。槽的类型由传递给槽创建例程的TupleTableSlotOps*决定。内置的槽类型有
 *
 * 1. 存储在磁盘缓冲区页面中的物理元组（TTSOpsBufferHeapTuple）
 * 2. 在palloc分配的内存中构造的物理元组（TTSOpsHeapTuple）
 * 3. 在palloc分配的内存中构造的“最小”物理元组（TTSOpsMinimalTuple）
 * 4. 由Datum/isnull数组组成的“虚拟”元组（TTSOpsVirtual）
 *
 *
 * 前两种情况类似，因为它们都处理“实际化”的元组，但资源管理是不同的。对于磁盘页面中的元组，我们需要在缓冲区上保持一个锁定，直到TupleTableSlot对元组的引用被释放；而对于palloc分配的元组，我们通常希望在TupleTableSlot对元组的引用被释放时立即释放该元组。
 *
 * “最小”元组的处理方式类似于palloc分配的常规元组。目前，最小元组永远不会存储在缓冲区，因此与情况1没有对应。注意，最小元组没有“系统列”。（实际上，它可以具有OID，但我们无需访问OID。）
 *
 * “虚拟”元组是一种优化，用来最小化计划节点嵌套中的物理数据复制。在槽中的实际化按引用传递的Datum指向与TupleTableSlot不直接关联的存储；通常它们指向存储在较低计划节点输出TupleTableSlot中的元组的一部分，或指向在计划节点的每个元组上下文中构造的函数结果。生成计划节点有责任确保这些资源在虚拟元组需要有效或被实际化的整个期间内不会被释放。还要注意，虚拟元组没有任何“系统列”。
 *
 * TupleTableSlot的Datum/isnull数组具有双重作用。对于虚拟槽，它们是权威数据。对于其他内置槽，这些数组包含从元组中提取的数据。（在这个状态下，任何按引用传递的Datum都指向物理元组。）提取的信息是“延迟”构建的，即只在需要时构建。这有助于避免对物理元组数据的重复提取。
 *
 * TupleTableSlot也可以是“空的”，用标志TTS_FLAG_EMPTY指示，表示不持有有效数据。这是一个新创建的槽的唯一有效状态，该槽尚未分配元组描述符。在这种状态下，tts_flags中不应设置TTS_SHOULDFREE，tts_tuple必须为NULL，tts_nvalid应为零。
 *
 * TupleTableSlot代码仅引用元组描述符，而不复制。ExecSetSlotDescriptor()的调用者负责提供一个描述符，该描述符的生命周期与槽相同。（通常，槽和描述符都位于每个查询内存中，并在查询结束时由内存上下文释放；因此没有必要提供任何额外机制来进行更多操作。但是，如果提供了引用计数的tupdesc，槽会增加tupdesc的引用计数。）
 *
 * 当在tts_flags中设置TTS_SHOULDFREE时，物理元组被视为由槽“拥有”，应在槽对元组的引用被释放时释放。
 *
 * tts_values/tts_isnull在槽创建时（当提供描述符时）或当描述符分配给槽时分配；它们的长度等于描述符的natts。
 *
 * TTS_FLAG_SLOW标志是槽变形堆元组的保存状态，不应被其他代码触摸。
 *----------
 */

/* true = slot is empty */
#define			TTS_FLAG_EMPTY			(1 << 1)
#define TTS_EMPTY(slot)	(((slot)->tts_flags & TTS_FLAG_EMPTY) != 0)

/* should pfree tuple "owned" by the slot? */
#define			TTS_FLAG_SHOULDFREE		(1 << 2)
#define TTS_SHOULDFREE(slot) (((slot)->tts_flags & TTS_FLAG_SHOULDFREE) != 0)

/* saved state for slot_deform_heap_tuple */
#define			TTS_FLAG_SLOW		(1 << 3)
#define TTS_SLOW(slot) (((slot)->tts_flags & TTS_FLAG_SLOW) != 0)

/* fixed tuple descriptor */
#define			TTS_FLAG_FIXED		(1 << 4)
#define TTS_FIXED(slot) (((slot)->tts_flags & TTS_FLAG_FIXED) != 0)

struct TupleTableSlotOps;
typedef struct TupleTableSlotOps TupleTableSlotOps;

/* base tuple table slot type */
typedef struct TupleTableSlot
{
	NodeTag		type;
#define FIELDNO_TUPLETABLESLOT_FLAGS 1
	uint16		tts_flags;		/* Boolean states */
#define FIELDNO_TUPLETABLESLOT_NVALID 2
	AttrNumber	tts_nvalid;		/* # of valid values in tts_values */
	const TupleTableSlotOps *const tts_ops; /* implementation of slot */
#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 4
	TupleDesc	tts_tupleDescriptor;	/* slot's tuple descriptor */
#define FIELDNO_TUPLETABLESLOT_VALUES 5
	Datum	   *tts_values;		/* current per-attribute values */
#define FIELDNO_TUPLETABLESLOT_ISNULL 6
	bool	   *tts_isnull;		/* 当前每个属性的 isnull 标志 */
	MemoryContext tts_mcxt;		/* slot itself is in this context */
	ItemPointerData tts_tid;	/* stored tuple's tid */
	Oid			tts_tableOid;	/* table oid of tuple */
} TupleTableSlot;

/* routines for a TupleTableSlot implementation */
struct TupleTableSlotOps
{
	/* Minimum size of the slot */
	size_t		base_slot_size;

	/* Initialization. */
	void		(*init) (TupleTableSlot *slot);

	/* Destruction. */
	void		(*release) (TupleTableSlot *slot);

	/*
	 * 清除槽的内容。只期望清除内容而不是元组描述符。通常，该回调的实现应该释放包含在槽中的元组所分配的内存。
	 */
	void		(*clear) (TupleTableSlot *slot);

	
/*
	 * 用槽中包含的元组的值填充 tts_values 和 tts_isnull 数组的前 natts 条目。
	 * 该函数可以在 natts 大于元组中可用属性的数量时调用，
	 * 在这种情况下，应该将 tts_nvalid 设置为返回的列数。
	 */
	void		(*getsomeattrs) (TupleTableSlot *slot, int natts);

	/*
	 * 返回给定系统属性的值作为 datum，并将 isnull 设置为 false，
	 * 如果它不是 NULL。如果槽类型不支持系统属性，则抛出错误。
	 */
	Datum		(*getsysattr) (TupleTableSlot *slot, int attnum, bool *isnull);

	/*
	 * 使槽的内容 solely 依赖于槽本身，而不是底层资源（如其他内存上下文、缓冲区等）。
	 */
	void		(*materialize) (TupleTableSlot *slot);

	/*
	 * 将源槽的内容复制到目标槽自己的上下文中。通过目标槽的回调进行调用。
	 */
	void		(*copyslot) (TupleTableSlot *dstslot, TupleTableSlot *srcslot);

	/*
	 * 返回一个“被”槽“拥有”的堆元组。释放堆元组所占内存的责任在于槽。
	 * 如果槽不能“拥有”堆元组，则不应实现此回调并应将其设置为 NULL。
	 */
	HeapTuple	(*get_heap_tuple) (TupleTableSlot *slot);

	/*
	 * 返回一个“被”槽“拥有”的最小元组。释放最小元组所占内存的责任在于槽。
	 * 如果槽不能“拥有”最小元组，则不应实现此回调并应将其设置为 NULL。
	 */
	MinimalTuple (*get_minimal_tuple) (TupleTableSlot *slot);

	/*
	 * 返回一个表示槽内容的堆元组的副本。副本需要在当前内存上下文中进行 palloc。
	 * 槽本身被期望保持不变。副本中不应有有意义的“系统列”。
	 * 副本不应被槽“拥有”，即调用者需要负责释放槽所占用的内存。
	 */
	HeapTuple	(*copy_heap_tuple) (TupleTableSlot *slot);

	/*
	 * 返回一个表示槽内容的最小元组的副本。副本需要在当前内存上下文中进行 palloc。
	 * 槽本身被期望保持不变。副本中不应有有意义的“系统列”。
	 * 副本不应被槽“拥有”，即调用者需要负责释放槽所占用的内存。
	 */
	MinimalTuple (*copy_minimal_tuple) (TupleTableSlot *slot);
};

/*
 * 针对各种类型的 TupleTableSlotOps 的预定义 TupleTableSlotOps。
 * 同样用于标识给定槽的类型。
 */
extern PGDLLIMPORT const TupleTableSlotOps TTSOpsVirtual;
extern PGDLLIMPORT const TupleTableSlotOps TTSOpsHeapTuple;
extern PGDLLIMPORT const TupleTableSlotOps TTSOpsMinimalTuple;
extern PGDLLIMPORT const TupleTableSlotOps TTSOpsBufferHeapTuple;

#define TTS_IS_VIRTUAL(slot) ((slot)->tts_ops == &TTSOpsVirtual)
#define TTS_IS_HEAPTUPLE(slot) ((slot)->tts_ops == &TTSOpsHeapTuple)
#define TTS_IS_MINIMALTUPLE(slot) ((slot)->tts_ops == &TTSOpsMinimalTuple)
#define TTS_IS_BUFFERTUPLE(slot) ((slot)->tts_ops == &TTSOpsBufferHeapTuple)


/*
 * 元组表槽的实现。
 */

typedef struct VirtualTupleTableSlot
{
	TupleTableSlot base;

	char	   *data;			/* 材料化槽的数据 */
} VirtualTupleTableSlot;

typedef struct HeapTupleTableSlot
{
	TupleTableSlot base;

#define FIELDNO_HEAPTUPLETABLESLOT_TUPLE 1
	HeapTuple	tuple;			/* 物理元组 */
#define FIELDNO_HEAPTUPLETABLESLOT_OFF 2
	uint32		off;			/* saved state for slot_deform_heap_tuple */
	HeapTupleData tupdata;		/* 存储元组的可选工作区 */
} HeapTupleTableSlot;

/* 在缓冲区中驻留的堆元组 */
typedef struct BufferHeapTupleTableSlot
{
	HeapTupleTableSlot base;

	/*
	 * 如果缓冲区不是 InvalidBuffer，则槽位持有指向
	 * 指定缓冲区页面的引用；当我们释放槽位对该缓冲区的
	 * 引用时丢弃该引用。 (TTS_FLAG_SHOULDFREE 在
	 * 这种情况下不应被设置，因为假定 tts_tuple
	 * 指向缓冲区内。)
	 */
	Buffer		buffer;			/* 元组的缓冲区，或 InvalidBuffer */
} BufferHeapTupleTableSlot;

typedef struct MinimalTupleTableSlot
{
	TupleTableSlot base;

	/*
	 * 在一个最小槽中，元组指向 minhdr，且该结构的字段
	 * 被正确设置以便访问最小元组；特别是，
	 * minhdr.t_data 指向 mintuple 之前的 MINIMAL_TUPLE_OFFSET
	 * 字节。这使得列提取可以将该情况与常规物理元组
	 * 一样处理。
	 */
#define FIELDNO_MINIMALTUPLETABLESLOT_TUPLE 1
	HeapTuple	tuple;			/* 元组包装器 */
	MinimalTuple mintuple;		/* 最小元组，或者如果没有则为 NULL */
	HeapTupleData minhdr;		/* 仅用于最小元组场景的工作区 */
#define FIELDNO_MINIMALTUPLETABLESLOT_OFF 4
	uint32		off;			/* saved state for slot_deform_heap_tuple */
} MinimalTupleTableSlot;

/*
 * TupIsNull -- 是 TupleTableSlot 为空吗？
 */
#define TupIsNull(slot) \
	((slot) == NULL || TTS_EMPTY(slot))

/* 在 executor/execTuples.c 中 */
extern TupleTableSlot *MakeTupleTableSlot(TupleDesc tupleDesc,
										  const TupleTableSlotOps *tts_ops);
extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable, TupleDesc desc,
										  const TupleTableSlotOps *tts_ops);
extern void ExecResetTupleTable(List *tupleTable, bool shouldFree);
extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc,
												const TupleTableSlotOps *tts_ops);
extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot);
extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc);
extern TupleTableSlot *ExecStoreHeapTuple(HeapTuple tuple,
										  TupleTableSlot *slot,
										  bool shouldFree);
extern void ExecForceStoreHeapTuple(HeapTuple tuple,
									TupleTableSlot *slot,
									bool shouldFree);
extern TupleTableSlot *ExecStoreBufferHeapTuple(HeapTuple tuple,
												TupleTableSlot *slot,
												Buffer buffer);
extern TupleTableSlot *ExecStorePinnedBufferHeapTuple(HeapTuple tuple,
													  TupleTableSlot *slot,
													  Buffer buffer);
extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup,
											 TupleTableSlot *slot,
											 bool shouldFree);
extern void ExecForceStoreMinimalTuple(MinimalTuple mtup, TupleTableSlot *slot,
									   bool shouldFree);
extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
extern void ExecStoreHeapTupleDatum(Datum data, TupleTableSlot *slot);
extern HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree);
extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot,
											  bool *shouldFree);
extern Datum ExecFetchSlotHeapTupleDatum(TupleTableSlot *slot);
extern void slot_getmissingattrs(TupleTableSlot *slot, int startAttNum,
								 int lastAttNum);
extern void slot_getsomeattrs_int(TupleTableSlot *slot, int attnum);


#ifndef FRONTEND

/*
 * 该函数强制槽位的 Datum/isnull 数组条目在至少
 * attnum 处有效。
 */
static inline void
slot_getsomeattrs(TupleTableSlot *slot, int attnum)
{
	if (slot->tts_nvalid < attnum)
		slot_getsomeattrs_int(slot, attnum);
}

/*
 * slot_getallattrs
 *		该函数强制槽位的 Datum/isnull 数组的所有条目有效。
 *		调用者可以直接从这些数组中提取数据，而不是使用
 *		slot_getattr。
 */
static inline void
slot_getallattrs(TupleTableSlot *slot)
{
	slot_getsomeattrs(slot, slot->tts_tupleDescriptor->natts);
}


/*
 * slot_attisnull
 *
 * 检测槽位的属性是否为 null，而无需实际获取
 * 它。
 */
static inline bool
slot_attisnull(TupleTableSlot *slot, int attnum)
{
	AssertArg(attnum > 0);

	if (attnum > slot->tts_nvalid)
		slot_getsomeattrs(slot, attnum);

	return slot->tts_isnull[attnum - 1];
}

/*
 * slot_getattr - 获取槽位内容的一个属性。
 */
static inline Datum
slot_getattr(TupleTableSlot *slot, int attnum,
			 bool *isnull)
{
	AssertArg(attnum > 0);

	if (attnum > slot->tts_nvalid)
		slot_getsomeattrs(slot, attnum);

	*isnull = slot->tts_isnull[attnum - 1];

	return slot->tts_values[attnum - 1];
}

/*
 * slot_getsysattr - 获取槽位当前元组的一个系统属性。
 *
 * 如果槽位类型不包含系统属性，将抛出错误。
 * 因此，在调用此函数之前，调用者应该确保
 * 槽位类型是支持系统属性的。
 */
static inline Datum
slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
{
	AssertArg(attnum < 0);		/* 调用者错误 */

	if (attnum == TableOidAttributeNumber)
	{
		*isnull = false;
		return ObjectIdGetDatum(slot->tts_tableOid);
	}
	else if (attnum == SelfItemPointerAttributeNumber)
	{
		*isnull = false;
		return PointerGetDatum(&slot->tts_tid);
	}

	/* 从底层元组中获取系统属性。 */
	return slot->tts_ops->getsysattr(slot, attnum, isnull);
}

/*
 * ExecClearTuple - 清除槽位的内容
 */
static inline TupleTableSlot *
ExecClearTuple(TupleTableSlot *slot)
{
	slot->tts_ops->clear(slot);

	return slot;
}

/* ExecMaterializeSlot - 将槽位强制进入 "具体化" 状态。
 *
 * 这使得槽位的元组成为不依赖于任何外部存储的本地副本
 * （即指向一个缓冲区，或在另一个内存上下文中有所分配）。
 *
 * 此操作的典型用途是准备一个计算元组以便存储到磁盘。
 * 原始数据可能是虚拟的，也可能不是，但在任何情况下
 * 我们需要一个私有副本供 heap_insert 使用。
 */
static inline void
ExecMaterializeSlot(TupleTableSlot *slot)
{
	slot->tts_ops->materialize(slot);
}

/*
 * ExecCopySlotHeapTuple - 返回在调用者上下文中分配的 HeapTuple
 */
static inline HeapTuple
ExecCopySlotHeapTuple(TupleTableSlot *slot)
{
	Assert(!TTS_EMPTY(slot));

	return slot->tts_ops->copy_heap_tuple(slot);
}

/*
 * ExecCopySlotMinimalTuple - 返回在调用者上下文中分配的 MinimalTuple
 */
static inline MinimalTuple
ExecCopySlotMinimalTuple(TupleTableSlot *slot)
{
	return slot->tts_ops->copy_minimal_tuple(slot);
}

/*
 * ExecCopySlot - 将一个槽位的内容复制到另一个槽位中。
 *
 * 如果源槽位的系统属性需要在目标槽位中访问，则目标槽位
 * 和源槽位类型需要匹配。
 */
static inline TupleTableSlot *
ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
{
	Assert(!TTS_EMPTY(srcslot));
	AssertArg(srcslot != dstslot);

	dstslot->tts_ops->copyslot(dstslot, srcslot);

	return dstslot;
}

#endif							/* FRONTEND */

#endif							/* TUPTABLE_H */
